﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RenderEngine;
using System.ComponentModel.Composition.Hosting;
using System.IO;
using System.ComponentModel.Composition;
using RenderEngineInterfaces;
using System.Threading.Tasks;
using DeepZoomPivotConstructor.VisualLib;
using System.Drawing;

namespace t_RenderEngine
{
    /// <summary>
    /// Test out doing some very simple stuff (like jpeg rendering, etc.)
    /// but nothing as complex as two file rendering (i.e. one exploding to another).
    /// </summary>
    [TestClass]
    public class t_RenderEngineSimplePlugins
    {
        public t_RenderEngineSimplePlugins()
        {
        }

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        [TestInitialize]
        public void TestInit()
        {
            t_TestingLibrary.Utilities.MyClassInit();
        }

        [TestCleanup]
        public void TestDone()
        {
            t_TestingLibrary.Utilities.MyClassDone();
        }

        [TestMethod]
        public void RenderWhenNoRenderItems()
        {
            RenderControl re = new RenderControl();
            Visual result = re.Render("junk.jpg");
            Assert.IsNull(result, "No render items - so how did this get rendered!?");
        }

        [TestMethod]
        public void RenderSimpleJPEG()
        {
            RenderControl re = new RenderControl();
            t_RenderTestingLibrary.Utilities.AddJPEGRender();
            t_TestingLibrary.Utilities.Compose(re);

            Visual result = re.Render("junk.jpg");
            Assert.IsNotNull(result, "Should have rendered something!");
            Assert.AreEqual((double)1.0, result.Width, "Width is incorrect");
            Assert.AreEqual((double)2.0, result.Height, "Height is not correct");
        }

        [TestMethod]
        public void RenderFile()
        {
            RenderControl re = new RenderControl();
            t_RenderTestingLibrary.Utilities.AddJPEGRender();
            t_TestingLibrary.Utilities.Compose(re);

            Visual result = re.Render(new FileInfo("bogus.jpg"));
            Assert.IsNull(result, "We have a string rendering guy up there, not a file rendering guy! This should not work.");
        }

        [TestMethod]
        public void RenderFileInfo()
        {
            RenderControl re = new RenderControl();
            t_RenderTestingLibrary.Utilities.AddJPEGRender();
            t_RenderTestingLibrary.Utilities.AddJPEGFileInfoRender();
            t_TestingLibrary.Utilities.Compose(re);

            Visual result = re.Render(new FileInfo("bogus.jpg"));
            Assert.IsNotNull(result, "Should have rendered something!");
            Assert.AreEqual((double)1.0, result.Width, "Width is incorrect");
            Assert.AreEqual((double)2.0, result.Height, "Height is not correct");
        }

        [TestMethod]
        public void RenderFileInfoBlockFileInfoTypePlugin()
        {
            RenderControl re = new RenderControl();
            t_RenderTestingLibrary.Utilities.AddJPEGRender();
            t_RenderTestingLibrary.Utilities.AddJPEGFileInfoRender();
            t_TestingLibrary.Utilities.Compose(re);

            Visual result = re.Render(new FileInfo("bogus.jpg"), new Type[] { typeof(t_RenderTestingLibrary.Utilities.RenderJpegFromFileInfo) });
            Assert.IsNull(result, "Should not have been anything to render!");
        }

        [TestMethod]
        public void RenderFileInfoWrongType()
        {
            RenderControl re = new RenderControl();
            t_RenderTestingLibrary.Utilities.AddJPEGRender();
            t_RenderTestingLibrary.Utilities.AddJPEGFileInfoRender();
            t_TestingLibrary.Utilities.Compose(re);

            Visual result = re.Render(new FileInfo("bogus.ps"));
            Assert.IsNull(result, "We have a string rendering guy up there, not a file rendering guy! This should not work.");
        }

        [TestMethod]
        public void RenderFileInfoWithTwoTypesDefined()
        {
            RenderControl re = new RenderControl();
            t_RenderTestingLibrary.Utilities.AddJPEGRender();
            t_RenderTestingLibrary.Utilities.AddJPEGFileInfoRender();
            t_RenderTestingLibrary.Utilities.AddPSFileInfoRender();
            t_TestingLibrary.Utilities.Compose(re);

            Visual result = re.Render(new FileInfo("bogus.ps"));
            Assert.IsNotNull(result, "Should have rendered something!");
            Assert.AreEqual((double)2.0, result.Width, "Width is incorrect");
            Assert.AreEqual((double)3.0, result.Height, "Height is not correct");
        }

        [TestMethod]
        public void RenderFileInfoCheckPriority()
        {
            RenderControl re = new RenderControl();
            t_RenderTestingLibrary.Utilities.AddPSFileInfoRender();
            t_RenderTestingLibrary.Utilities.AddPSFileInfoRenderHigh();
            t_TestingLibrary.Utilities.Compose(re);

            Visual result = re.Render(new FileInfo("bogus.ps"));
            Assert.IsNotNull(result, "Should have rendered something!");
            Assert.AreEqual((double)3.0, result.Width, "Width is incorrect");
            Assert.AreEqual((double)4.0, result.Height, "Height is not correct");
        }

        /// <summary>
        /// return null for a new rendered item!
        /// </summary>
        [Export(typeof(IRenderItem))]
        [RenderInfo(typeof(string))]
        class RenderToNullTask : IRenderItem
        {
            #region IRenderItem Members

            public Task<Visual> Render(object what)
            {
                return null;
            }

            public RenderPluginPriority RenderPriority(object what)
            {
                return RenderPluginPriority.RPPGeneric;
            }

            #endregion
        }

        /// <summary>
        /// return null for a rendered item after task call
        /// </summary>
        [Export(typeof(IRenderItem))]
        [RenderInfo(typeof(string))]
        class RenderToNullInTask : IRenderItem
        {
            #region IRenderItem Members

            public Task<Visual> Render(object what)
            {
                return Task<Visual>.Factory.StartNew(() => null);
            }

            public RenderPluginPriority RenderPriority(object what)
            {
                return RenderPluginPriority.RPPGeneric;
            }

            #endregion
        }

        [TestMethod]
        public void RenderReturnsNullFuture()
        {
            t_TestingLibrary.Utilities.AddPart(new RenderToNullTask());
            RenderControl re = new RenderControl();
            t_TestingLibrary.Utilities.Compose(re);
            Assert.IsNull(re.Render("hi there"), "Expected null back from simple null rendering!");
        }

        [TestMethod]
        public void RenderReturnsNull()
        {
            t_TestingLibrary.Utilities.AddPart(new RenderToNullInTask());
            RenderControl re = new RenderControl();
            t_TestingLibrary.Utilities.Compose(re);
            var result = re.Render("hi there");
            Assert.IsNull(result, "Result should be null!");
        }

        /// <summary>
        /// Throw when we try to do a priority!
        /// </summary>
        [Export(typeof(IRenderItem))]
        [RenderInfo(typeof(string))]
        class ThrowingRender : IRenderItem
        {
            public Task<Visual> Render(object what)
            {
                throw new NotImplementedException();
            }

            public RenderPluginPriority RenderPriority(object what)
            {
                throw new NotImplementedException();
            }
        }

        [TestMethod]
        public void TestThrowDuringPriority()
        {
            t_TestingLibrary.Utilities.AddPart(new ThrowingRender());
            RenderControl re = new RenderControl();
            t_TestingLibrary.Utilities.Compose(re);

            Assert.AreEqual(null, re.Render("hi there"), "Should be a can't do it for the string!");
        }

        [Export(typeof(IRenderItem))]
        [RenderInfo(typeof(string))]
        class RenderBombLater : IRenderItem
        {
            public Task<Visual> Render(object what)
            {
                return Task<Visual>.Factory.StartNew(() =>
                {
                    throw new InvalidOperationException("ops!");
                });
            }

            public RenderPluginPriority RenderPriority(object what)
            {
                return RenderPluginPriority.RPPGeneric;
            }
        }

        [TestMethod]
        public void TestRenderWithExceptionInTask()
        {
            RenderControl re = new RenderControl();
            t_TestingLibrary.Utilities.AddPart(new RenderBombLater());
            t_TestingLibrary.Utilities.Compose(re);

            Visual result = re.Render("junk.jpg");
            Assert.IsNull(result, "Expected a null result when throw occured");

            var resultNext = re.RenderToFuture("junk.jpg", new Type[0], false);
            Assert.IsNotNull(resultNext, "Not expecting a null return when rendering to a future");
            Assert.IsNull(resultNext.Result, "Expected a null result when we actually run the task!");
        }

        [TestMethod]
        public void TestForceCompose()
        {
            RenderControl re = new RenderControl();
            t_RenderTestingLibrary.Utilities.AddJPEGRender();
            t_TestingLibrary.Utilities.Compose(re);

            var result = re.Render("junk.jpg", forceAsComposable:true);

            Assert.IsTrue(result.IsCompositionUnit, "The visual should be marked as a compositional unit!");
            Assert.IsNotNull(result.CompositionInfo.MetaData, "The meta-data should have been auto-set");
        }

        [TestMethod]
        public void TestForceAndReturnNullTask()
        {
            RenderControl re = new RenderControl();
            t_TestingLibrary.Utilities.AddPart(new RenderToNullTask());
            t_TestingLibrary.Utilities.Compose(re);

            var result = re.Render("junk.jpg", forceAsComposable: true);

            Assert.IsNull(result, "Expected a null to come back from the rendered item!");
        }

        [TestMethod]
        public void TestForceAndReturnNull()
        {
            RenderControl re = new RenderControl();
            t_TestingLibrary.Utilities.AddPart(new RenderToNullInTask());
            t_TestingLibrary.Utilities.Compose(re);

            var result = re.Render("junk.jpg", forceAsComposable: true);

            Assert.IsNull(result, "Expected a null to come back from the rendered item!");
        }

        [TestMethod]
        public void TestForceAndThrow()
        {
            RenderControl re = new RenderControl();
            t_TestingLibrary.Utilities.AddPart(new ThrowingRender());
            t_TestingLibrary.Utilities.Compose(re);

            var result = re.Render("junk.jpg", forceAsComposable: true);

            Assert.IsNull(result, "Expected null when we throw coming back!");
        }

        /// <summary>
        /// Render a visual to be some weird size for testing null sizes
        /// </summary>
        [Export(typeof(IRenderItem))]
        [RenderInfo(typeof(string))]
        private class RenderToSizedVisual : IRenderItem
        {
            public double XSize { get; set; }
            public double YSize { get; set; }

            public Task<Visual> Render(object what)
            {
                return Task<Visual>.Factory.StartNew(() => new VisualBitmapImage(XSize, YSize, 100, Color.Black));
            }

            public RenderPluginPriority RenderPriority(object what)
            {
                return RenderPluginPriority.RPPGeneric;
            }
        }

        [TestMethod]
        public void TestRenderXZero()
        {
            RenderControl re = new RenderControl();
            t_TestingLibrary.Utilities.AddPart(new RenderToSizedVisual() { XSize = 0.0, YSize = 1.0 });
            t_TestingLibrary.Utilities.Compose(re);

            var result = re.Render("junk.jpg");
            Assert.IsNull(result, "Expected a null result for a visual that was infinitly thin");
        }

        [TestMethod]
        public void TestRenderyZero()
        {
            RenderControl re = new RenderControl();
            t_TestingLibrary.Utilities.AddPart(new RenderToSizedVisual() { XSize = 1.0, YSize = 0.0 });
            t_TestingLibrary.Utilities.Compose(re);

            var result = re.Render("junk.jpg");
            Assert.IsNull(result, "Expected a null result for a visual that was infinitly short");
        }
    }
}
